/*!
 * template.js v0.7.1 (https://github.com/yanhaijing/template.js)
 * API https://github.com/yanhaijing/template.js/blob/master/doc/api.md
 * Copyright 2015 yanhaijing. All Rights Reserved
 * Licensed under MIT (https://github.com/yanhaijing/template.js/blob/master/MIT-LICENSE.txt)
 */
;(function(root, factory) {
  var template = factory(root);
  if (typeof define === 'function' && define.amd) {
    // AMD
    define('template', function() {
      return template;
    });
  } else if (typeof exports === 'object') {
    // Node.js
    module.exports = template;
  } else {
    // Browser globals
    var _template = root.template;

    template.noConflict = function() {
      if (root.template === template) {
        root.template = _template;
      }

      return template;
    };
    root.template = template;
  }
}(this, function(root) {
  'use strict';
  var o = {
    sTag: '<%',//开始标签
    eTag: '%>',//结束标签
    compress: false,//是否压缩html
    escape: true, //默认输出是否进行HTML转义
    error: function (e) {}//错误回调
  };
  var functionMap = {}; //内部函数对象
  //修饰器前缀
  var modifierMap = {
    '': function (param) {return nothing(param)},
    'h': function (param) {return encodeHTML(param)},
    'u': function (param) {return encodeURI(param)}
  };

  var toString = {}.toString;
  var slice = [].slice;
  function type(x) {
    if(x === null){
      return 'null';
    }

    var t= typeof x;

    if(t !== 'object'){
      return t;
    }

    var c = toString.call(x).slice(8, -1).toLowerCase();
    if(c !== 'object'){
      return c;
    }

    if(x.constructor==Object){
      return c;
    }

    return 'unknown';
  }

  function isObject(obj) {
    return type(obj) === 'object';
  }
  function isFunction(fn) {
    return type(fn) === 'function';
  }
  function isString(str) {
    return type(str) === 'string';
  }
  function extend() {
    var target = arguments[0] || {};
    var arrs = slice.call(arguments, 1);
    var len = arrs.length;

    for (var i = 0; i < len; i++) {
      var arr = arrs[i];
      for (var name in arr) {
        target[name] = arr[name];
      }

    }
    return target;
  }
  function clone() {
    var args = slice.call(arguments);
    return extend.apply(null, [{}].concat(args));
  }
  function nothing(param) {
    return param;
  }
  function encodeHTML(source) {
    return String(source)
        .replace(/&/g,'&amp;')
        .replace(/</g,'&lt;')
        .replace(/>/g,'&gt;')
        .replace(/\\/g,'&#92;')
        .replace(/"/g,'&quot;')
        .replace(/'/g,'&#39;');
  }
  function compress(html) {
    return html.replace(/\s+/g, ' ').replace(/<!--[\w\W]*?-->/g, '');
  }
  function consoleAdapter(cmd, msg) {
    typeof console !== 'undefined' && console[cmd] && console[cmd](msg);
  }
  function handelError(e) {
    var message = 'template.js error\n\n';

    for (var key in e) {
      message += '<' + key + '>\n' + e[key] + '\n\n';
    }
    message += '<message>\n' + e.message + '\n\n';
    consoleAdapter('error', message);

    o.error(e);
    function error() {
      return 'template.js error';
    }
    error.toString = function () {
      return '__code__ = "template.js error"';
    }
    return error;
  }
  function parse(tpl, opt) {
    var code = '';
    var sTag = opt.sTag;
    var eTag = opt.eTag;
    var escape = opt.escape;
    function parsehtml(line) {
      // 单双引号转义，换行符替换为空格
      line = line.replace(/('|")/g, '\\$1');
      var lineList = line.split('\n');
      var code = '';
      for (var i = 0; i < lineList.length; i++) {
        code += ';__code__ += ("' + lineList[i] + (i === lineList.length - 1 ? '")\n' : '\\n")\n');
      }
      return code;
    }
    function parsejs(line) {
      //var reg = /^(:?)(.*?)=(.*)$/;
      var reg = /^(?:=|(:.*?)=)(.*)$/
      var html;
      var arr;
      var modifier;

      // = := :*=
      // :h=123 [':h=123', 'h', '123']
      if (arr = reg.exec(line)) {
        html = arr[2]; // 输出
        if (Boolean(arr[1])) {
          // :开头
          modifier = arr[1].slice(1);
        } else {
          // = 开头
          modifier = escape ? 'h' : '';
        }

        return ';__code__ += __modifierMap__["' + modifier + '"](typeof (' + html + ') !== "undefined" ? (' + html + ') : "")\n';
      }

      //原生js
      return ';' + line + '\n';
    }

    var tokens = tpl.split(sTag);

    for (var i = 0, len = tokens.length; i < len; i++) {
      var token = tokens[i].split(eTag);

      if (token.length === 1) {
        code += parsehtml(token[0]);
      } else {
        code += parsejs(token[0], true);
        if (token[1]) {
          code += parsehtml(token[1]);
        }
      }
    }
    return code;
  }
  function compiler(tpl, opt) {
    var mainCode = parse(tpl, opt);

    var headerCode = '\n' +
        '    var html = (function (__data__, __modifierMap__) {\n' +
        '        var __str__ = "", __code__ = "";\n' +
        '        for(var key in __data__) {\n' +
        '            __str__+=("var " + key + "=__data__[\'" + key + "\'];");\n' +
        '        }\n' +
        '        eval(__str__);\n\n';

    var footerCode = '\n' +
        '        ;return __code__;\n' +
        '    }(__data__, __modifierMap__));\n' +
        '    return html;\n';

    var code = headerCode + mainCode + footerCode;
    code = code.replace(/[\r]/g, ' '); // ie 7 8 会报错，不知道为什么
    try {
      var Render = new Function('__data__', '__modifierMap__', code);
      Render.toString = function () {
        return mainCode;
      }
      return Render;
    } catch(e) {
      e.temp = 'function anonymous(__data__, __modifierMap__) {' + code + '}';
      throw e;
    }
  }
  function compile(tpl, opt) {
    opt = clone(o, opt);

    try {
      var Render = compiler(tpl, opt);
    } catch(e) {
      e.name = 'CompileError';
      e.tpl = tpl;
      e.render = e.temp;
      delete e.temp;
      return handelError(e);
    }

    function render(data) {
      data = clone(functionMap, data);
      try {
        var html = Render(data, modifierMap);
        html = opt.compress ? compress(html) : html;
        return html;
      } catch(e) {
        e.name = 'RenderError';
        e.tpl = tpl;
        e.render = Render.toString();
        return handelError(e)();
      }
    }

    render.toString = function () {
      return Render.toString();
    };
    return render;
  }
  function template(tpl, data) {
    if (typeof tpl !== 'string') {
      return '';
    }

    var fn = compile(tpl);
    if (!isObject(data)) {
      return fn;
    }

    return fn(data);
  }

  template.config = function (option) {
    if (isObject(option)) {
      o = extend(o, option);
    }
    return clone(o);
  };

  template.registerFunction = function(name, fn) {
    if (!isString(name)) {
      return clone(functionMap);
    }
    if (!isFunction(fn)) {
      return functionMap[name];
    }

    return functionMap[name] = fn;
  }
  template.unregisterFunction = function (name) {
    if (!isString(name)) {
      return false;
    }
    delete functionMap[name];
    return true;
  }

  template.registerModifier = function(name, fn) {
    if (!isString(name)) {
      return clone(modifierMap);
    }
    if (!isFunction(fn)) {
      return modifierMap[name];
    }

    return modifierMap[name] = fn;
  }
  template.unregisterModifier = function (name) {
    if (!isString(name)) {
      return false;
    }
    delete modifierMap[name];
    return true;
  }

  template.__encodeHTML = encodeHTML;
  template.__compress = compress;
  template.__handelError = handelError;
  template.__compile = compile;
  template.version = '0.7.1';
  return template;
}));
